// ==UserScript==
// @name            Better Mobile Twitter
// @version         5
// @namespace       http://ellab.org
// @author          angusdev
// @description     To enhance the mobile twitter page
// @include         http://m.twitter.com/home
// ==/UserScript==

/*
Author: Angus http://angusdev.mysinablog.com/
              http://angusdev.blogspot.com/
Date:   2009-03-17

Version history:
5    17-Mar-2009    Add direct messages sidebar
                    Add replies sidebar
                    Add reply button for tweets
                    Detect if cannot load the page and quit the script
                    ExpandUrl supports tr.im, short.to, twurl.nl
                    ExpandUrl supports url with ellipse
                    ExpandUrl matches url better for ff.im, youtube and hellotxt
                    ExpandUrl image supports ping.fm and hellotxt
                    ExpandUrl will directly show the continue url if need to login google account
                    ExpandUrl will add spaces in really long url to make sure it will wrap
                    Add a clear TwitPic cache button
                    Fix the bug that exception raised when url stored in sessionStorage is empty string
4    20-Feb-2009    Auto expand short URL
                    Image and Youtube preview
                    Filter by user
                    Show new tweets count in window title
                    Fine tune the checking of invalid char
3    18-Nov-2008    Add the character count function
                    Check for new tweets on background
                    Add an on-off button
                    Make the input box wider
                    Show error message if AJAX call fails
                    Change to a OO approach
2    06-Nov-2008    Support Chrome
1    13-Oct-2008    First release to userscripts.org
*/

function BetterMobileTwitter() {
  this.isChrome = false;
  this.enabled = true;
  this.loading = false;
  this.page = 1;
  this.lastMessage = '';
  this.myname = '';
  this.onsrc = '';
  this.offsrc = '';
  this.loadingsrc = '';

  this.nsResolver = {
    lookupNamespaceURI:function (prefix) {
      if (prefix == "html") {
        return "http://www.w3.org/1999/xhtml";
      }
      else {
        return "";
      }
    }
  };
  this.expandUrlMap = [
    {name:'tinyurl',     func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/tinyurl\.com\/[a-zA-z0-9]+$/},
    {name:'snipurl',     func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/snipurl\.com\/[a-zA-z0-9]+$/},
    {name:'pingfm',      func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/ping\.fm\/[a-zA-z0-9]+$/},
    {name:'ffim',        func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/ff\.im\/[a-zA-z0-9\-\|]+$/},
    {name:'trim',        func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/tr\.im\/[a-zA-z0-9]+$/},
    {name:'isgd',        func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/is\.gd\/[a-zA-z0-9]+$/},
    {name:'bitly',       func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/bit\.ly\/[a-zA-z0-9]+$/},
    {name:'twurl',       func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/twurl\.nl\/[a-zA-z0-9]+$/},
    {name:'shortto',     func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/short\.to\/[a-zA-z0-9]+$/},
    {name:'hellotxt',    func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/hellotxt\.com\/l\/[a-zA-z0-9]+$/},
    {name:'hellotxttxt', func:this.expandUrl_hellotxt,    ajax:true,  regex:/http:\/\/hellotxt\.com\/[a-zA-z0-9]+$/},
    //{name:'funp',        func:this.expandUrl_tinyurl,     ajax:true,  regex:/http:\/\/funp\.com\//},
    {name:'twitpic',     func:this.expandUrl_twitpic,     ajax:true,  regex:/http:\/\/twitpic\.com\/[a-zA-z0-9]+$/},
    {name:'tapulous',    func:this.expandUrl_tapulous,    ajax:true,  regex:/http:\/\/twinkle\.tapulous\.com\/index\.php\?hash=/},
    {name:'flickr',      func:this.expandUrl_flickr,      ajax:true,  regex:/(www\.)?flickr\.com\/photos/},
    {name:'pingfmimg',   func:this.expandUrl_pingfmimg,   ajax:true,  regex:/http:\/\/ping\.fm\/p\/[a-zA-z0-9]+$/},
    {name:'hellotxtimg', func:this.expandUrl_hellotxtimg, ajax:true,  regex:/http:\/\/hellotxt\.com\/i\/[a-zA-z0-9]+$/},
    {name:'youtube',     func:this.expandUrl_youtube,     ajax:false, regex:/http:\/\/[a-z]*\.youtube\.com\//},
    {name:'img',         func:this.expandUrl_img,         ajax:false, regex:/http:\/\/.*\.(gif|jpg|png)$/},
    {name:'googlelogin', func:this.expandUrl_googlelogin, ajax:false, regex:/^https?:\/\/[^\/]*\.google\.com?(\.[a-zA-Z]{1,2})?\/accounts\/ServiceLogin\?/},
    {name:'longurl',     func:this.expandUrl_longurl,     ajax:false, regex:/.{101}/},
  ];
}

BetterMobileTwitter.prototype.encodeHTML = function(t) {
  t = t.replace(/&/g, '&amp;');
  t = t.replace(/\"/g, '&quot;');
  t = t.replace(/</g, '&lt;');
  t = t.replace(/>"/g, '&gt;');

  return t;
}

BetterMobileTwitter.prototype.init = function() {
  if (navigator.userAgent.match(/Chrome/)) {
    this.enabled = document.location.href == 'http://m.twitter.com/home';
    this.isChrome = true;
  }

  if (this.enabled && document.body) this.functionPrinciple();
}

BetterMobileTwitter.prototype.extract = function(s, prefix, suffix) {
  var i = s.indexOf(prefix);
  if (i >= 0) {
    s = s.substring(i + prefix.length);
  }
  else {
    return '';
  }
  if (suffix) {
    i = s.indexOf(suffix);
    if (i >= 0) {
      s = s.substring(0, i);
    }
    else {
      return '';
    }
  }
  return s;
}

BetterMobileTwitter.prototype.removeInvalidChar = function(t) {
  // Some tweets has special character, need to remove it
  for (var i=0;i<t.length;i++) {
    if (t.charCodeAt(i) < 32 && t.charCodeAt(i) != 10 && t.charCodeAt(i) != 13 && t.charCodeAt(i) != 9) {
      t = t.substring(0, i) + t.substring(i+1);
      --i;
    }
  }

  return t;
}

BetterMobileTwitter.prototype.extractTweetsHTML = function(fullt) {
  return this.removeInvalidChar(this.extract(fullt, '<ul>', '</ul>'));
}

BetterMobileTwitter.prototype.nextPage = function() {
  if (this.loading) {
    return;
  }

  this.loading = true;
  document.getElementById('bmt-scrolldetector').innerHTML = 'Loading more tweets...';

  var bmt = this;
  var client = new XMLHttpRequest();
  client.onreadystatechange = function() {
    if (this.readyState == 4) {
      if (this.status == 200) {
        var t = bmt.extractTweetsHTML(this.responseText);

        var targetul = document.getElementById('bmt-tweetsdiv').getElementsByTagName('ul')[0];
        pageli = document.createElement('li');
        pageli.innerHTML = 'Page ' + (bmt.page + 1);
        targetul.appendChild(pageli);
        var ulholder = document.createElement('ul');
        ulholder.innerHTML = t;
        var lilist = ulholder.getElementsByTagName('li');
        while (lilist.length) {
          lilist[0].addEventListener('mouseover', function(e) { bmt.onMouseOverOutTweets(e.target, true); }, false);
          lilist[0].addEventListener('mouseout', function(e) { bmt.onMouseOverOutTweets(e.target, false); }, false);
          targetul.appendChild(lilist[0]);
        }

        bmt.loading = false;
        document.getElementById('bmt-scrolldetector').innerHTML = '';

        // add user filter
        var filter = document.getElementById('bmt-userfilter');
        while (t) {
          var li = bmt.extract(t, '<li>', '</li>');
          if (li) {
            bmt.addUserFilter(filter, li);
          }

          t = bmt.extract(t, '</li>');
        }
        bmt.onUserFilterChanged(filter);

        bmt.page++;
        bmt.expandUrl(1);
      }
      else {
        document.getElementById('bmt-scrolldetector').innerHTML = 'Error ' + this.status;
      }
    }
  }
  client.open('GET', 'http://m.twitter.com/account/home.mobile?page=' + (bmt.page + 1));
  client.send(null);
}

BetterMobileTwitter.prototype.loadReplies = function() {
  var replyDiv = document.getElementById('bmt-replydiv');
  replyDiv.innerHTML = 'Loading replies ...';

  var bmt = this;
  var client = new XMLHttpRequest();
  client.onreadystatechange = function() {
    if (this.readyState == 4) {
      if (this.status == 200) {
        var t = bmt.extractTweetsHTML(this.responseText);
        replyDiv.innerHTML = '<div class="s" style="font-size:133%;"><b>replies</b></div><ul>' + t + '</ul>';
      }
      else {
        replyDiv.innerHTML = 'Error ' + this.status;
      }
    }
  }
  client.open('GET', 'http://m.twitter.com/replies');
  client.send(null);
}


BetterMobileTwitter.prototype.loadDirectMessage = function(displayCount) {
  var directMessageDiv = document.getElementById('bmt-directdiv');
  directMessageDiv.innerHTML = 'Loading direct messages ...';

  var bmt = this;
  var client = new XMLHttpRequest();
  client.onreadystatechange = function() {
    if (this.readyState == 4) {
      if (this.status == 200) {
        var olbody = bmt.extract(this.responseText, '<ol class="statuses" id="timeline">', '</ol>');
        //t = t.replace(/id="[^"]*"/g, '');
        //t = t.replace(/<img[^>]*>/g, '');
        var cont = true;
        var count = 0;
        var html = '';
        while (cont) {
          var t = bmt.extract(olbody, '<li', '</li>');
          if (t) {
            t = bmt.extract(t, 'status-body">', '<span class="action');
            // span class="published" is the publish time, change to <small> as used in mobile version
            t = t.replace(/<span class="published">([^<]*)<\/span>/, ' <small>$1</small>');
            // remove all span tags
            t = t.replace(/<\/?span[^>]*>/g, '');
            // remove all image tags
            t = t.replace(/<img[^>]*>/g, '');
            // remove all class attribute
            t = t.replace(/\s+class="[^"]*"/g, '');
            // remove all title attribute
            t = t.replace(/\s+title="[^"]*"/g, '');
            // add spaces after the username
            t = t.replace(/(<\/strong>)/, '$1 ');
            // finally remove invalid chars
            t = bmt.removeInvalidChar(t);
            html = html + '<li' + (++count > displayCount?' style="display:none;"':'') + '>' + t + '</li>';

            olbody = bmt.extract(olbody, '</li>');
          }
          else {
            cont = false;
          }
        }
        directMessageDiv.innerHTML = '<div class="s" style="font-size:133%;"><b>direct messages</b>' +
                                     (count > displayCount?' <a id="bmt-directdiv-expand" href="javascript:void(0)">[+]</a>':'') +
                                     '</div><ul>' + html + '</ul>';
        var expandLink = document.getElementById('bmt-directdiv-expand');
        if (expandLink) {
          expandLink.addEventListener('click', function(e) {
            var lilist = directMessageDiv.getElementsByTagName('li');
            for (var i=displayCount;i<lilist.length;i++) {
              lilist[i].style.display = '';
            }
            e.target.parentNode.removeChild(e.target);
          }, false);
        }
      }
      else {
        directMessageDiv.innerHTML = 'Error ' + this.status;
      }
    }
  }
  client.open('GET', 'http://m.twitter.com/direct_messages');
  client.send(null);
}

BetterMobileTwitter.prototype.calcOffsetTop = function(e) {
  var top = 0;
  do {
    if (!isNaN(e.offsetTop)) top += e.offsetTop;
  } while (e = e.offsetParent);

  return top;
}

BetterMobileTwitter.prototype.detectScroll = function() {
  if (!this.enabled) return;
  var scrollTop = this.isChrome?document.body.scrollTop:document.documentElement.scrollTop;
  if (this.calcOffsetTop(document.getElementById('bmt-scrolldetector')) < scrollTop + document.documentElement.clientHeight) {
    this.nextPage();
  }
}

BetterMobileTwitter.prototype.statusMessageChanged = function(e) {
  document.getElementById('bmt-wordcount').innerHTML = 140 - e.target.value.length;
}

BetterMobileTwitter.prototype.checkUpdate = function() {
  var bmt = this;
  if (!bmt.enabled) {
    window.setTimeout(function() {bmt.checkUpdate(bmt);}, 60000);
    return;
  }

  var client = new XMLHttpRequest();
  client.onreadystatechange = function() {
    if (this.readyState == 4) {
      if (this.status == 200) {
        var newTweetsCount = 0;
        var fullt = this.responseText;
        var t = bmt.extract(fullt, '<ul>', '</ul>');
        while (t) {
          var li = bmt.extract(t, '<li>', '</li>');
          li = li?li.replace(/<small>[^<]*<\/small>/, ''):'';
          if (li) {
            document.getElementById('bmt-htmlholder').innerHTML = li;
            li = document.getElementById('bmt-htmlholder').innerHTML;
            if (li == bmt.lastMessage) {
              break;
            }
            else {
              newTweetsCount++;
            }
          }

          t = bmt.extract(t, '</li>');
        }
        if (newTweetsCount) {
          document.getElementById('bmt-checkupdate').innerHTML = newTweetsCount + ' new tweet' + (newTweetsCount>1?'s':'');
          if (document.title.match(/^\(\d+\)/)) {
            document.title = document.title.replace(/^\(\d+\)/, '(' + newTweetsCount + ')')
          }
          else {
            document.title = '(' + newTweetsCount + ') ' + document.title;
          }
        }
      }
      else if (this.status) {
        document.getElementById('bmt-checkupdate').innerHTML = 'Error ' + this.status;
      }

      window.setTimeout(function() {bmt.checkUpdate(bmt);}, 60000);
    }
  }
  client.open('GET', 'http://m.twitter.com/account/home.mobile');
  client.send(null);
}

BetterMobileTwitter.prototype.addUserFilter = function(filter, li) {
  // get user list
  var nameres = li.match(/^<a[^>]*>([^<]*)</);
  if (nameres) {
    var name = nameres[1];
    for (var i=1; i<filter.options.length; i++) {
      if (filter.options[i].value.toLowerCase() == name.toLowerCase()) return;
      if (filter.options[i].value.toLowerCase() > name.toLowerCase()) break;
    }

    var option = document.createElement('option');
    option.text = name;
    option.value = name;
    filter.options.add(option, i);
  }
}

BetterMobileTwitter.prototype.onUserFilterChanged = function(filter) {
  var name = filter.options[filter.selectedIndex].value;

  var lis = document.getElementById('bmt-tweetsdiv').getElementsByTagName('li');
  for (var i=0; i<lis.length; i++) {
    if (filter.selectedIndex == 0 || lis[i].innerHTML.match('href="\/' + name + '"')) {
      lis[i].style.display = '';
    }
    else {
      lis[i].style.display = 'none';
    }
  }
}

BetterMobileTwitter.prototype.sessionStorageWrapper = function(url, obj, key, func) {
  key = key + '|';
  if (obj.value && obj.value.length > key.length && obj.value.substring(0, key.length) == key) {
    return obj.value.substring(key.length + 1);
  }

  var data = func();
  if (window.sessionStorage) {
    window.sessionStorage.setItem(url, key + '|' + data);
  }

  return data;
}

BetterMobileTwitter.prototype.sessionStorageWrapper_image = function(a, url, obj, key, func) {
  var imgUrl = this.sessionStorageWrapper(url, obj, key, func);

  if (imgUrl) {
    this.expandUrl_image(a, url, imgUrl);
  }
  else{
    this.expandUrl(1);
  }
}

BetterMobileTwitter.prototype.expandUrl_tinyurl = function(bmt, a, url, t) {
  var finalUrl = bmt.sessionStorageWrapper(url, t, 'tinyurl', function() {
    return t.finalUrl;
  });

  if (finalUrl && url != finalUrl) {
    a.innerHTML = bmt.encodeHTML(decodeURIComponent(finalUrl));
  }
  a.setAttribute('bmt-finalurl', finalUrl);

  if (!bmt.expandOneUrl(a)) {
    bmt.expandUrl(1);
  }
}

BetterMobileTwitter.prototype.expandUrl_image = function(a, url, imgsrc) {
  if (imgsrc[0] == '/') {
    imgsrc = url.match(/https?:\/\/[^\/]*/)[0] + imgsrc;
  }
  var img = document.createElement('img');
  img.setAttribute('style', 'border:none; margin-left:5px; height:70px;');
  img.src = imgsrc;
  a.appendChild(img);

  this.expandUrl(1);
}

BetterMobileTwitter.prototype.expandUrl_hellotxt = function(bmt, a, url, t) {
  t = bmt.extract(bmt.extract(t.responseText, '<div class="history-row big">'), '<p>', '</p>');
  if (t) {
    a.innerHTML = t;
  }

  bmt.expandUrl(1);
}

BetterMobileTwitter.prototype.expandUrl_twitpic = function(bmt, a, url, t) {
  bmt.sessionStorageWrapper_image(a, url, t, 'twitpic', function() {
    return bmt.extract(bmt.extract(t.responseText, '<img id="pic"'), 'src="', '"');
  });
}

BetterMobileTwitter.prototype.expandUrl_tapulous = function(bmt, a, url, t) {
  var res = url.match(/[\?|&]hash=([a-zA-Z0-9]*)(&.*$)?/);
  if (res) {
    // show the first 4 hash, then follow by ...
    a.innerHTML = a.innerHTML.replace(/([\?|&amp;]hash=[a-zA-Z0-9]{4})[a-zA-Z0-9]*/, '$1...');
  }

  bmt.sessionStorageWrapper_image(a, url, t, 'tapulous', function() {
    return bmt.extract(bmt.extract(bmt.extract(t.responseText, '<div id="post">'), '</div>'), 'img src="', '"');
  });
}

BetterMobileTwitter.prototype.expandUrl_pingfmimg = function(bmt, a, url, t) {
  bmt.sessionStorageWrapper_image(a, url, t, 'pingfmimg', function() {
    return bmt.extract(bmt.extract(t.responseText, 'background:url(/_images/layout/loading.gif)'), '<img src="', '"');
  });
}


BetterMobileTwitter.prototype.expandUrl_hellotxtimg = function(bmt, a, url, t) {
  bmt.sessionStorageWrapper_image(a, url, t, 'hellotxtimg', function() {
    return bmt.extract(bmt.extract(t.responseText, '<div class="status_pic'), '<img src="', '"');
  });
}

BetterMobileTwitter.prototype.expandUrl_flickr = function(bmt, a, url, t) {
  bmt.sessionStorageWrapper_image(a, url, t, 'flickr', function() {
    var pid = url.match(/flickr\.com\/photos\/[^\/]+\/(\d+)/);
    if (pid) {
      return bmt.extract(bmt.extract(t.responseText, '<div id="photoImgDiv' + pid[1] + '"'), 'src="', '"');
    }
    return '';
  });
}

BetterMobileTwitter.prototype.expandUrl_youtube = function(bmt, a, url) {
  var res = url.match(/[\?|&]v=([a-zA-Z0-9_\-]*)(&.*$)?/);
  if (res) {
    a.innerHTML = a.innerHTML.replace(/([\?|&]v=[a-zA-Z0-9_\-]*)(&.*$)/, '$1&amp;...');

    bmt.expandUrl_image(a, url, 'http://i4.ytimg.com/vi/' + res[1] + '/default.jpg');
  }
  else {
    bmt.expandUrl(1);
  }
}

BetterMobileTwitter.prototype.expandUrl_img = function(bmt, a, url) {
  bmt.expandUrl_image(a, url, url);
}

BetterMobileTwitter.prototype.expandUrl_googlelogin = function(bmt, a, url) {
  var res = url.match(/&continue=(.*)/);
  if (res) {
    a.innerHTML = bmt.encodeHTML(decodeURIComponent(res[1]));
  }

  bmt.expandUrl(1);
}

BetterMobileTwitter.prototype.expandUrl_longurl = function(bmt, a, url) {
  var brokenurl = '';
  var html = a.innerHTML;
  while (html.length > 100) {
    brokenurl = brokenurl + (brokenurl.length?' ':'') + html.substring(0, 100);
    html = html.substring(100);
  }
  brokenurl = brokenurl + (brokenurl.length?' ':'') + html.substring(0, 100);

  a.innerHTML = bmt.encodeHTML(brokenurl);

  bmt.expandUrl(1);
}

BetterMobileTwitter.prototype.expandOneUrl_ajaxWrapper = function(bmt, a, url, func) {
  if (window.sessionStorage) {
    // t.finalUrl only
    var t = window.sessionStorage.getItem(url);
    if (t) {
      func(bmt, a, url, t);
      return;
    }
  }

  var img = document.createElement('img');
  img.src = bmt.loadingsrc;
  img.setAttribute('style', 'margin-left:5px;');
  a.appendChild(img);

  GM_xmlhttpRequest({
    method: 'GET',
    url: url,
    onload: function(t) {
      a.removeChild(img);
      func(bmt, a, url, t);
    }
  });
}

BetterMobileTwitter.prototype.expandOneUrl = function(a) {
  var url = a.getAttribute('bmt-finalurl');
  if (!url || url == '') {
    url = a.href;
  }
  for (var j=0;j<this.expandUrlMap.length;j++) {
    if (url.match(this.expandUrlMap[j].regex)) {
      if (this.expandUrlMap[j].ajax) {
        this.expandOneUrl_ajaxWrapper(this, a, url, this.expandUrlMap[j].func);
      }
      else {
        this.expandUrlMap[j].func(this, a, url);
      }

      return true;
    }
  }

  var urlres = a.innerHTML.match(/(.*)\.\.\.+$/);
  if (urlres) {
    if (url.substring(0, urlres[1].length) == urlres[1]) {
      a.innerHTML = this.encodeHTML(url);
    }
  }

  return false;
}

BetterMobileTwitter.prototype.expandUrl = function(maxRun) {
  if (this.isChrome) return;

  var res = document.evaluate("//html:div[@id='bmt-tweetsdiv']//html:a[not(@bmt-expandurl)]", document, this.nsResolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  var loadcount = 0;
  for (var i=0;i<res.snapshotLength;i++) {
    var a = res.snapshotItem(i);
    if (!a.getAttribute('bmt-expandurl')) {
      a.setAttribute('bmt-expandurl', 'true');
      if (this.expandOneUrl(a)) {
        if (++loadcount >= maxRun) return;
      }
    }
  }
}

BetterMobileTwitter.prototype.onMouseOverOutTweets = function(obj, isover) {
  while (obj && obj.tagName.toUpperCase() != 'LI') {
    obj = obj.parentNode;
  }
  if (obj) {
    var actionspan = obj.getElementsByTagName('span');
    if (actionspan.length > 0 && actionspan[actionspan.length - 1].getAttribute('bmt-actionspan')) {
      actionspan = actionspan[actionspan.length - 1];
    }
    else if (isover && this.myname != obj.getElementsByTagName('a')[0].textContent) {
      actionspan = document.createElement('span');
      actionspan.setAttribute('bmt-actionspan', 'true');
      // tweets div is 80% width, so use right: 22%
      actionspan.setAttribute('style', 'position: absolute; right: 22%;');

      var replybtn = document.createElement('img');
      replybtn.src = 'http://static.twitter.com/images/icon_reply.gif';
      replybtn.setAttribute('style', 'cursor:pointer; padding:5px;');
      replybtn.addEventListener('click', function(e) {
        var status = document.getElementById('status');
        if (status) {
          var replyto = '@' + obj.getElementsByTagName('a')[0].textContent;
          // ignore if already start with the replyto string
          if (!status.value.match('^' + replyto)) {
            status.value = replyto + ' ' + status.value.replace(replyto, '');
            status.focus();
          }
        }
      }, false);

      actionspan.appendChild(replybtn);

      obj.appendChild(actionspan);
    }
    else {
      actionspan = null;
    }

    if (actionspan) {
      actionspan.style.visibility = isover?'visible':'hidden';
    }

    obj.style.backgroundColor = isover?'#f7f7f7':'';
  }
}

BetterMobileTwitter.prototype.functionPrinciple = function() {
  // check if it is a mobile version
  if (document.getElementById('dim-screen')) return;

  var status = document.getElementById('status');

  // check if any connection error
  if (!status) return;

  var bmt = this;

  this.page = document.location.href.match(/page=(\d+)/);
  this.page = this.page?paresInt(this.page[1], 10):1;

  // get user name
  var username = document.evaluate("//html:a[@accesskey='2']/@href", document, this.nsResolver, XPathResult.STRING_TYPE, null).stringValue;
  if (username) {
    var res = username.match(/twitter\.com\/(.*)$/);
    if (res) {
      this.myname = res[1];
    }
  }

  // add replies layer
  var tweetsDiv = document.createElement('div');
  var rightBarDiv = document.createElement('div');
  var tweetsUl = document.getElementsByTagName('ul')[0];

  tweetsDiv.setAttribute('style', 'width:80%;');
  tweetsDiv.setAttribute('id', 'bmt-tweetsdiv');


  rightBarDiv.setAttribute('style', 'float:right; width:19%; margin-left:1%; margin-right:3px; ');

  var directDiv = document.createElement('div');
  directDiv.setAttribute('style', 'min-height: 100px; padding:5px; font-size: 75%; ' +
                                 'background:#f9ffe8; border:1px solid #87bc44; ' +
                                 '-moz-border-radius:5px; -webkit-border-radius: 5px;');
  directDiv.setAttribute('id', 'bmt-directdiv');
  directDiv.innerHTML = 'BBDBD';
  rightBarDiv.appendChild(directDiv);

  var replyDiv = document.createElement('div');
  replyDiv.setAttribute('style', 'min-height: 100px; padding:5px; margin-top:8px; font-size: 75%; ' +
                                 'background:#f9ffe8; border:1px solid #87bc44; ' +
                                 '-moz-border-radius:5px; -webkit-border-radius: 5px;');
  replyDiv.setAttribute('id', 'bmt-replydiv');
  rightBarDiv.appendChild(replyDiv);

  tweetsUl.parentNode.insertBefore(rightBarDiv, tweetsUl);
  tweetsUl.parentNode.insertBefore(tweetsDiv, tweetsUl);
  tweetsDiv.appendChild(tweetsUl);

  this.loadDirectMessage(2);
  this.loadReplies();

  // modify status window
  if (status) {
    // remove the BR between status editbox and update button
    var br = status.nextSibling;
    while (br && br.tagName != 'br') br = br.nextSibling;
    if (br && br.parentNode) {
      br.parentNode.removeChild(br);
      status.style.marginRight = '7px';
    }
    status.style.width = '500px';

    // show remaining char
    var wordCount = document.createElement('span');
    wordCount.setAttribute('id', 'bmt-wordcount');
    wordCount.innerHTML = '140';
    status.parentNode.appendChild(wordCount);
    status.addEventListener('keyup', this.statusMessageChanged, false);
    status.addEventListener('blur', this.statusMessageChanged, false);
    status.addEventListener('focus', this.statusMessageChanged, false);

    // dropdown for user filter
    var filter = document.createElement('select');
    filter.setAttribute('id', 'bmt-userfilter');
    filter.setAttribute('style', 'margin-left: 30px;');
    filter.addEventListener('change', function(e) { bmt.onUserFilterChanged(e.target)}, false);

    var option = document.createElement('option');
    option.text = ' --- Filter --- ';
    option.value = ' ';
    filter.options.add(option, 0);

    // generate user filter list
    var lis = document.getElementsByTagName('li');
    for (var i=0;i<lis.length;i++) {
      this.addUserFilter(filter, lis[i].innerHTML);
    }

    status.parentNode.appendChild(filter);
  }

  // setup mouseover and mouseout event
  var tweetslilist = document.evaluate("//html:div[@id='bmt-tweetsdiv']//html:li", document, this.nsResolver, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  for (var i=0;i<tweetslilist.snapshotLength;i++) {
    tweetslilist.snapshotItem(i).addEventListener('mouseover', function(e) { bmt.onMouseOverOutTweets(e.target, true); }, false);
    tweetslilist.snapshotItem(i).addEventListener('mouseout', function(e) { bmt.onMouseOverOutTweets(e.target, false); }, false);
  }

  // change the older link for scroll detector
  var res = document.getElementsByTagName('a');
  for (var i=res.length-1; i>=0; i--) {
    if (res[i].getAttribute('accesskey') == 6) {
      var scrollDetector = res[i].parentNode;
      scrollDetector.setAttribute('id', 'bmt-scrolldetector');
      scrollDetector.innerHTML = '';
    }
  }

  // get last message
  var lastMessageLi = document.getElementsByTagName('li');
  if (lastMessageLi.length) {
    this.lastMessage = lastMessageLi[0].innerHTML;
    this.lastMessage = this.lastMessage.replace(/<small[^<]*<\/small>/, '');
  }

  // command panel
  var commandPanel = document.createElement('div');
  commandPanel.setAttribute('style', 'position: absolute; right: 0px; top: 0px;');
  document.getElementsByTagName('div')[0].appendChild(commandPanel);

  var checkUpdateDiv = document.createElement('div');
  checkUpdateDiv.setAttribute('id', 'bmt-checkupdate');
  checkUpdateDiv.setAttribute('style', 'float:left; margin:3px 5px 0px 0px;');
  var checkUpdateContainer = document.createElement('span');
  checkUpdateContainer.appendChild(checkUpdateDiv);
  commandPanel.appendChild(checkUpdateContainer);

  // clear twitpic session
  if (window.sessionStorage) {
    var clearTwitpic = document.createElement('input');
    clearTwitpic.type = 'button';
    clearTwitpic.value = 'Clear TwitPic cache';
    clearTwitpic.className = 'b';
    clearTwitpic.setAttribute('style', 'vertical-align:top; margin:3px 5px 0px 0px; font-size:10pt;');
    clearTwitpic.title = 'TwitPic image URL may change after a while, clear cache to reload the image thumbnail';
    clearTwitpic.addEventListener('click', function(e) {
      var found = false;
      for (var i=0;i<window.sessionStorage.length;i++) {
        var domkey = window.sessionStorage.key(i);
        var domvalue = window.sessionStorage.getItem(domkey);
        if (domvalue.value.match(/^twitpic\|/)) {
          sessionStorage.removeItem(domkey);
          found = true;
        }
      }
      if (found) {
        document.location.reload();
      }
    }, false);
    commandPanel.appendChild(clearTwitpic);
  }

  // on off button
  var onoff = document.createElement('img');
  onoff.src = bmt.onsrc;
  onoff.setAttribute('style', 'cursor:pointer;');
  onoff.addEventListener('click', function(e) {
    bmt.enabled = !bmt.enabled;
    e.target.src = bmt.enabled?bmt.onsrc:bmt.offsrc;
  }, false);
  commandPanel.appendChild(onoff);

  // an element to convert text to HTML, for comparing last message
  var htmlholder = document.createElement('span');
  htmlholder.setAttribute('id', 'bmt-htmlholder');
  htmlholder.style.display = 'none';
  document.body.appendChild(htmlholder);

  // expand URL
  this.expandUrl(3);

  window.setInterval(function() {bmt.detectScroll(bmt);}, 500);
  window.setTimeout(function() {bmt.checkUpdate(bmt);}, 60000);
}
new BetterMobileTwitter().init();